Loading R Packages
First, load these packages, since all of them are required to execute
further steps. If you can’t load them / are missing one, please use
install.packages() to re install or download them, and then run the
code. Note that Seurat requires specific versions to work correctly; we
typically use v.4.4.0, but the latest version (5.1.0) could also
work.
suppressMessages(library(mclust))
suppressMessages(library(MatrixExtra))
suppressMessages(library(ggplot2))
suppressMessages(library(dplyr))
suppressMessages(library(sctransform))
suppressMessages(library(future))
suppressMessages(library(Seurat))
suppressMessages(library(cowplot))
suppressMessages(library(stringr))
suppressMessages(library(patchwork))
suppressMessages(library(Matrix))
suppressMessages(library(Scillus))
suppressMessages(library(MAST))
suppressMessages(library(tidyr))
suppressMessages(library(ggthemes))
suppressMessages(library(magrittr))
suppressMessages(library(SeuratData))
suppressMessages(library(SeuratObject))
suppressMessages(library(devtools))
suppressMessages(library(htmltools))
suppressMessages(library(ScaledMatrix))
suppressMessages(library(sparseMatrixStats))
suppressMessages(library(DelayedMatrixStats))
suppressMessages(library(BPCells))
suppressMessages(library(tidyr))
Loading Seurat Objects
Load the seurat object that contains the single cell data obtained
from samples. A seurat object is a dataset that contains the original
data of the donors, along with cell types identified, diabetic status,
etc. Load the metadata to look at all information contained in this
seurat object in various categories. In this case,
reharm_SCT_2024Jun18_small is a downsampled version of a processed
seurat object containing human pancreatic islet sample data.
reharm_SCT_2024Jun18_small <- readRDS("~/AS/objects/reharm_SCT_2024Jun18_small.rds")
reharm_SCT_2024Jun18_small
An object of class Seurat
68799 features across 10000 samples within 3 assays
Active assay: RNA (36601 features, 3000 variable features)
3 layers present: counts, data, scale.data
2 other assays present: Protein, SCT
4 dimensional reductions calculated: pca, harmony, umap, DecontX_NoGroup_umap
reharm_SCT_2024Jun18_small@meta.data
Viewing Cell Types in Seurat Objects
We are interested in viewing different cell types, so we use Idents()
and levels(). This shows us different cell types associated with
endocrine and inflammatory processes such as alpha, beta, delta-gamma,
macrophages, mast cells, etc.
Idents(reharm_SCT_2024Jun18_small) <- "cell_type"
levels(reharm_SCT_2024Jun18_small)
[1] "Alpha" "Alpha_Prolif" "Beta" "Endocrine_MAP2"
[5] "Dedifferentiated_Endocrine" "Delta_Gamma" "Macrophages" "Mast"
[9] "Lymphocytes" "B_Cells" "Myofibroblasts" "Pericytes"
[13] "Endothelial" "Schwann" "Ductal" "Acinar"
[17] "LowQuality_Endocrine" "LowQuality_Endothelial" "LowQuality_Exo" "LowQuality"
Deleting Cell Types Not Relevant to Seurat Objects
We view the cell types present in our large seurat object. We see
that there are cell types associated with low quality, and it would be
beneficial to the dataset if we removed them. To do this, we follow the
code below.
reharm_SCT_2024Jun18_small <- subset(reharm_SCT_2024Jun18_small, idents = c("LowQuality_Endocrine", "LowQuality_Endothelial", "LowQuality_Exo", "LowQuality"), invert = TRUE)
Visualizing Cell Types Using Dimensional Plots.
Now that we have removed low quality cell types, we re-construct a
DimPlot with an updated registry of cell types for our reference.
DimPlot(reharm_SCT_2024Jun18_small)+ggtitle("ALL CELL TYPES")

Subsetting Cells of Interest from Seurat Object
Now, we need to make a subset from this data that contains only mast
cells, because we are interested in characterizing mast cells in
diabetic patients. This generates a new seurat object called
mast_cells_cluster. If you want to subset some other type of cells, use
the same syntax but edit to include your cell type instead.
mast_cells_cluster <- subset(reharm_SCT_2024Jun18_small, idents = "Mast")
levels(mast_cells_cluster)
[1] "Mast"
Seurat Clusters and How to View Them
Within mast cells, we need to view the different clusters that have
been generated under this seurat object. Seurat clusters are usually
named 1, 2, 3, etc. that are used to cluster the cells together for more
easier organization.
Idents(mast_cells_cluster) <- "seurat_clusters"
levels(mast_cells_cluster)
[1] "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "10"
Single Cell RNA Sequencing of Seurat Clusters
Now, it’s time to prepare this data for scRNA seq analysis. This is
done through running a PCA analysis and then running a UMAP analysis to
obtain a UMAP plot (can also use TSNE, as they both test linear
dimensionality to produce a DimPlot). Then, we use FindNeighbors to
compute a shared nearest neighbors (SNN) graph, which helps in
clustering.
mast_cells_cluster <- RunPCA(mast_cells_cluster, dims = 1:30, verbose = FALSE) %>%
RunTSNE(dims = 1:30, verbose = FALSE) %>%
FindNeighbors(verbose = TRUE) %>%
FindClusters(resolution = 0.1, verbose = TRUE)
Computing nearest neighbor graph
Computing SNN
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 2266
Number of edges: 79677
Running Louvain algorithm...
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9346
Number of communities: 3
Elapsed time: 0 seconds
Saving the Analyzed Cells as a new Seurat Object
We have now obtained a seurat object with our required scRNA seq
parameters. Save this object using saveRDS().
saveRDS(mast_cells_cluster, "mast_cells_cluster.rds")
Visualizing This Object as a TSNE Dimensional Plot
Here’s a TSNE plot to exhibit our clustering results:
TSNEPlot(mast_cells_cluster, group.by = "seurat_clusters")

And a TSNE plot to show the same, grouped by heterogenous donors, and
conditions:
TSNEPlot(mast_cells_cluster, group.by = "Donor")

TSNEPlot(mast_cells_cluster, group.by = "Condition")

Finding Markers for Differential Expressed Genes
Now we need to find marker genes for this processed mast cell cluster
object under the 3 clusters identified. Obtaining these marker genes
will help us downstream in seeing which genes are expressed when and
why. They are called differential expressed genes (DEG).
Idents(mast_cells_cluster) <- "seurat_clusters"
mast_cell_markers <- Seurat::FindAllMarkers(mast_cells_cluster, only.pos = FALSE, test.use = "roc")
Save markers as table.
write.csv(mast_cell_markers, "mast_cell_markers_clean.csv")
head(mast_cell_markers_clean)
Classifying DEG Markers Based on Diabetic Conditions:
Change Idents() to look at differential expressed genes by condition,
because we want to view the marker genes we obtained before based on
Lean condition (which is non-diabetic), and T2D condition. We ignore
Obese group because it contains both diabetic and non-diabetic samples,
which has shown to produce discrepancy in results.
Idents(mast_cells_cluster) <- "Condition"
levels(mast_cells_cluster)
[1] "Lean" "Obese" "PreT2D" "T2D"
Visualizing Such Markers through TSNE Dimensional Plots
Let’s now view the analysis we’ve done using TSNEPlot. We first
obtain a general plot of all conditions for reference. You can also do
this using UMAPPlot or DimPlot.
TSNEPlot(mast_cells_cluster,group.by = "Condition")

Subsetting Markers into Diabetic Conditions of Interest
We further subset the seurat object mast_cells_cluster based on the
conditions we want, which are lean and T2D. We then reprocess
condition_subset into clusters for accurate visualization.
mast_cells_cluster@graphs <- list()
condition_subset <- subset(mast_cells_cluster, idents = c("Lean", "T2D"))
condition_subset <- RunPCA(condition_subset, dims = 1:30, verbose = FALSE) %>%
RunTSNE(dims = 1:30, verbose = FALSE) %>%
FindNeighbors(verbose = TRUE) %>%
FindClusters(resolution = 0.1, verbose = TRUE)
Computing nearest neighbor graph
Computing SNN
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 1410
Number of edges: 48291
Running Louvain algorithm...
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9153
Number of communities: 3
Elapsed time: 0 seconds
TSNEPlot(condition_subset)+ggtitle("Lean and T2D Conditions")

Finding DEGs Based on these Specific Diabetic Conditions
Find DEGs between Lean and T2D conditions.
condition_subset <- Seurat::FindAllMarkers(condition_subset, only.pos = FALSE, test.use = "roc")
This can be saved both as a CSV for later use on Excel.
write.csv(condition_subset, "condition_subset.csv")
Organizing DEG Lists
In this final step, the DEG list we’ve obtained, condition_subset,
needs to be organized better for easier analysis. We organize it in two
ways. First is based on average log2FC, because increasing positive log
fold change denotes up regulation in genes. Second is based on removing
discrepant genes such as mitochondrial (MT) and ribosome (RP) genes
using the function !grepl.
condition_subset$genes <- rownames(condition_subset)
condition_subset <- dplyr::filter(condition_subset, !grepl("MT-", condition_subset$genes))
condition_subset <- dplyr::filter(condition_subset, !grepl("RP", condition_subset$genes))
sorted_mast_cells_condition_subset <- condition_subset[order(condition_subset$avg_log2FC, decreasing = TRUE), ]
head(sorted_mast_cells_condition_subset)
Saving and Viewing an Organized DEG Table:
After organizing through average log2FC and discrepant genes, we save
as csv file and export to Excel for further analysis.
View(sorted_mast_cells_condition_subset)
write.csv(sorted_mast_cells_condition_subset, "sorted_mast_cells_condition_subset.csv")
LS0tCnRpdGxlOiAic2NSTkEgU2VxdWVuY2luZyBBbmFseXNpcyBvZiBNYXN0IENlbGxzIGluIERpYWJldGljIFBhbmNyZWF0aWMgTWljcm9lbnZpcm9ubWVudCIKYXV0aG9yOiAiQWtpbGVzaCBTaGFua2FyIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdApEYXRlOiBKdWx5IDIwMjQKZWRpdG9yX29wdGlvbnM6CiAgbWFya2Rvd246CiAgICB3cmFwOiA3MgotLS0KIyMjIExvYWRpbmcgUiBQYWNrYWdlcwpGaXJzdCwgbG9hZCB0aGVzZSBwYWNrYWdlcywgc2luY2UgYWxsIG9mIHRoZW0gYXJlIHJlcXVpcmVkIHRvIGV4ZWN1dGUgZnVydGhlciBzdGVwcy4gSWYgeW91IGNhbid0IGxvYWQgdGhlbSAvIGFyZSBtaXNzaW5nIG9uZSwgcGxlYXNlIHVzZSBpbnN0YWxsLnBhY2thZ2VzKCkgdG8gcmUgaW5zdGFsbCBvciBkb3dubG9hZCB0aGVtLCBhbmQgdGhlbiBydW4gdGhlIGNvZGUuIE5vdGUgdGhhdCBTZXVyYXQgcmVxdWlyZXMgc3BlY2lmaWMgdmVyc2lvbnMgdG8gd29yayBjb3JyZWN0bHk7IHdlIHR5cGljYWxseSB1c2Ugdi40LjQuMCwgYnV0IHRoZSBsYXRlc3QgdmVyc2lvbiAoNS4xLjApIGNvdWxkIGFsc28gd29yay4KYGBge3J9CnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShtY2x1c3QpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoTWF0cml4RXh0cmEpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZ2dwbG90MikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShkcGx5cikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShzY3RyYW5zZm9ybSkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShmdXR1cmUpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoU2V1cmF0KSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGNvd3Bsb3QpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoc3RyaW5ncikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShwYXRjaHdvcmspKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoTWF0cml4KSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KFNjaWxsdXMpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoTUFTVCkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeSh0aWR5cikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShnZ3RoZW1lcykpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShtYWdyaXR0cikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShTZXVyYXREYXRhKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KFNldXJhdE9iamVjdCkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShkZXZ0b29scykpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShodG1sdG9vbHMpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoU2NhbGVkTWF0cml4KSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHNwYXJzZU1hdHJpeFN0YXRzKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KERlbGF5ZWRNYXRyaXhTdGF0cykpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShCUENlbGxzKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHRpZHlyKSkKYGBgCgoKIyMjIExvYWRpbmcgU2V1cmF0IE9iamVjdHMgCkxvYWQgdGhlIHNldXJhdCBvYmplY3QgdGhhdCBjb250YWlucyB0aGUgc2luZ2xlIGNlbGwgZGF0YSBvYnRhaW5lZCBmcm9tIHNhbXBsZXMuIEEgc2V1cmF0IG9iamVjdCBpcyBhIGRhdGFzZXQgdGhhdCBjb250YWlucyB0aGUgb3JpZ2luYWwgZGF0YSBvZiB0aGUgZG9ub3JzLCBhbG9uZyB3aXRoIGNlbGwgdHlwZXMgaWRlbnRpZmllZCwgZGlhYmV0aWMgc3RhdHVzLCBldGMuIExvYWQgdGhlIG1ldGFkYXRhIHRvIGxvb2sgYXQgYWxsIGluZm9ybWF0aW9uIGNvbnRhaW5lZCBpbiB0aGlzIHNldXJhdCBvYmplY3QgaW4gdmFyaW91cyBjYXRlZ29yaWVzLiBJbiB0aGlzIGNhc2UsIHJlaGFybV9TQ1RfMjAyNEp1bjE4X3NtYWxsIGlzIGEgZG93bnNhbXBsZWQgdmVyc2lvbiBvZiBhIHByb2Nlc3NlZCBzZXVyYXQgb2JqZWN0IGNvbnRhaW5pbmcgaHVtYW4gcGFuY3JlYXRpYyBpc2xldCBzYW1wbGUgZGF0YS4gCmBgYHtyfQpyZWhhcm1fU0NUXzIwMjRKdW4xOF9zbWFsbCA8LSByZWFkUkRTKCJ+L0FTL29iamVjdHMvcmVoYXJtX1NDVF8yMDI0SnVuMThfc21hbGwucmRzIikKCnJlaGFybV9TQ1RfMjAyNEp1bjE4X3NtYWxsCgpyZWhhcm1fU0NUXzIwMjRKdW4xOF9zbWFsbEBtZXRhLmRhdGEKYGBgCgojIyMgVmlld2luZyBDZWxsIFR5cGVzIGluIFNldXJhdCBPYmplY3RzCldlIGFyZSBpbnRlcmVzdGVkIGluIHZpZXdpbmcgZGlmZmVyZW50IGNlbGwgdHlwZXMsIHNvIHdlIHVzZSBJZGVudHMoKSBhbmQgbGV2ZWxzKCkuIFRoaXMgc2hvd3MgdXMgZGlmZmVyZW50IGNlbGwgdHlwZXMgYXNzb2NpYXRlZCB3aXRoIGVuZG9jcmluZSBhbmQgaW5mbGFtbWF0b3J5IHByb2Nlc3NlcyBzdWNoIGFzIGFscGhhLCBiZXRhLCBkZWx0YS1nYW1tYSwgbWFjcm9waGFnZXMsIG1hc3QgY2VsbHMsIGV0Yy4KYGBge3J9CklkZW50cyhyZWhhcm1fU0NUXzIwMjRKdW4xOF9zbWFsbCkgPC0gImNlbGxfdHlwZSIKbGV2ZWxzKHJlaGFybV9TQ1RfMjAyNEp1bjE4X3NtYWxsKQpgYGAKCgojIyMgRGVsZXRpbmcgQ2VsbCBUeXBlcyBOb3QgUmVsZXZhbnQgdG8gU2V1cmF0IE9iamVjdHMKV2UgdmlldyB0aGUgY2VsbCB0eXBlcyBwcmVzZW50IGluIG91ciBsYXJnZSBzZXVyYXQgb2JqZWN0LiBXZSBzZWUgdGhhdCB0aGVyZSBhcmUgY2VsbCB0eXBlcyBhc3NvY2lhdGVkIHdpdGggbG93IHF1YWxpdHksIGFuZCBpdCB3b3VsZCBiZSBiZW5lZmljaWFsIHRvIHRoZSBkYXRhc2V0IGlmIHdlIHJlbW92ZWQgdGhlbS4gVG8gZG8gdGhpcywgd2UgZm9sbG93IHRoZSBjb2RlIGJlbG93LgpgYGB7cn0KcmVoYXJtX1NDVF8yMDI0SnVuMThfc21hbGwgPC0gc3Vic2V0KHJlaGFybV9TQ1RfMjAyNEp1bjE4X3NtYWxsLCBpZGVudHMgPSBjKCJMb3dRdWFsaXR5X0VuZG9jcmluZSIsICJMb3dRdWFsaXR5X0VuZG90aGVsaWFsIiwgIkxvd1F1YWxpdHlfRXhvIiwgIkxvd1F1YWxpdHkiKSwgaW52ZXJ0ID0gVFJVRSkKYGBgCgoKIyMjIFZpc3VhbGl6aW5nIENlbGwgVHlwZXMgVXNpbmcgRGltZW5zaW9uYWwgUGxvdHMuCk5vdyB0aGF0IHdlIGhhdmUgcmVtb3ZlZCBsb3cgcXVhbGl0eSBjZWxsIHR5cGVzLCB3ZSByZS1jb25zdHJ1Y3QgYSBEaW1QbG90IHdpdGggYW4gdXBkYXRlZCByZWdpc3RyeSBvZiBjZWxsIHR5cGVzIGZvciBvdXIgcmVmZXJlbmNlLgpgYGB7cn0KRGltUGxvdChyZWhhcm1fU0NUXzIwMjRKdW4xOF9zbWFsbCkrZ2d0aXRsZSgiQUxMIENFTEwgVFlQRVMiKQpgYGAKIyMjIFN1YnNldHRpbmcgQ2VsbHMgb2YgSW50ZXJlc3QgZnJvbSBTZXVyYXQgT2JqZWN0Ck5vdywgd2UgbmVlZCB0byBtYWtlIGEgc3Vic2V0IGZyb20gdGhpcyBkYXRhIHRoYXQgY29udGFpbnMgb25seSBtYXN0IGNlbGxzLCBiZWNhdXNlIHdlIGFyZSBpbnRlcmVzdGVkIGluIGNoYXJhY3Rlcml6aW5nIG1hc3QgY2VsbHMgaW4gZGlhYmV0aWMgcGF0aWVudHMuIFRoaXMgZ2VuZXJhdGVzIGEgbmV3IHNldXJhdCBvYmplY3QgY2FsbGVkIG1hc3RfY2VsbHNfY2x1c3Rlci4gSWYgeW91IHdhbnQgdG8gc3Vic2V0IHNvbWUgb3RoZXIgdHlwZSBvZiBjZWxscywgdXNlIHRoZSBzYW1lIHN5bnRheCBidXQgZWRpdCB0byBpbmNsdWRlIHlvdXIgY2VsbCB0eXBlIGluc3RlYWQuCmBgYHtyfQptYXN0X2NlbGxzX2NsdXN0ZXIgPC0gc3Vic2V0KHJlaGFybV9TQ1RfMjAyNEp1bjE4X3NtYWxsLCBpZGVudHMgPSAiTWFzdCIpCmxldmVscyhtYXN0X2NlbGxzX2NsdXN0ZXIpCmBgYAoKIyMjIFNldXJhdCBDbHVzdGVycyBhbmQgSG93IHRvIFZpZXcgVGhlbQpXaXRoaW4gbWFzdCBjZWxscywgd2UgbmVlZCB0byB2aWV3IHRoZSBkaWZmZXJlbnQgY2x1c3RlcnMgdGhhdCBoYXZlIGJlZW4gZ2VuZXJhdGVkIHVuZGVyIHRoaXMgc2V1cmF0IG9iamVjdC4gU2V1cmF0IGNsdXN0ZXJzIGFyZSB1c3VhbGx5IG5hbWVkIDEsIDIsIDMsIGV0Yy4gdGhhdCBhcmUgdXNlZCB0byBjbHVzdGVyIHRoZSBjZWxscyB0b2dldGhlciBmb3IgbW9yZSBlYXNpZXIgb3JnYW5pemF0aW9uLgpgYGB7cn0KSWRlbnRzKG1hc3RfY2VsbHNfY2x1c3RlcikgPC0gInNldXJhdF9jbHVzdGVycyIKbGV2ZWxzKG1hc3RfY2VsbHNfY2x1c3RlcikKYGBgCgojIyMgU2luZ2xlIENlbGwgUk5BIFNlcXVlbmNpbmcgb2YgU2V1cmF0IENsdXN0ZXJzCk5vdywgaXQncyB0aW1lIHRvIHByZXBhcmUgdGhpcyBkYXRhIGZvciBzY1JOQSBzZXEgYW5hbHlzaXMuIFRoaXMgaXMgZG9uZSB0aHJvdWdoIHJ1bm5pbmcgYSBQQ0EgYW5hbHlzaXMgYW5kIHRoZW4gcnVubmluZyBhIFVNQVAgYW5hbHlzaXMgdG8gb2J0YWluIGEgVU1BUCBwbG90IChjYW4gYWxzbyB1c2UgVFNORSwgYXMgdGhleSBib3RoIHRlc3QgbGluZWFyIGRpbWVuc2lvbmFsaXR5IHRvIHByb2R1Y2UgYSBEaW1QbG90KS4gVGhlbiwgd2UgdXNlIEZpbmROZWlnaGJvcnMgdG8gY29tcHV0ZSBhIHNoYXJlZCBuZWFyZXN0IG5laWdoYm9ycyAoU05OKSBncmFwaCwgd2hpY2ggaGVscHMgaW4gY2x1c3RlcmluZy4KYGBge3J9Cm1hc3RfY2VsbHNfY2x1c3RlciA8LSBSdW5QQ0EobWFzdF9jZWxsc19jbHVzdGVyLCBkaW1zID0gMTozMCwgdmVyYm9zZSA9IEZBTFNFKSAlPiUKICBSdW5UU05FKGRpbXMgPSAxOjMwLCB2ZXJib3NlID0gRkFMU0UpICU+JQogIEZpbmROZWlnaGJvcnModmVyYm9zZSA9IFRSVUUpICU+JQogIEZpbmRDbHVzdGVycyhyZXNvbHV0aW9uID0gMC4xLCB2ZXJib3NlID0gVFJVRSkKYGBgCgoKIyMjIFNhdmluZyB0aGUgQW5hbHl6ZWQgQ2VsbHMgYXMgYSBuZXcgU2V1cmF0IE9iamVjdApXZSBoYXZlIG5vdyBvYnRhaW5lZCBhIHNldXJhdCBvYmplY3Qgd2l0aCBvdXIgcmVxdWlyZWQgc2NSTkEgc2VxIHBhcmFtZXRlcnMuIFNhdmUgdGhpcyBvYmplY3QgdXNpbmcgc2F2ZVJEUygpLgpgYGB7cn0Kc2F2ZVJEUyhtYXN0X2NlbGxzX2NsdXN0ZXIsICJtYXN0X2NlbGxzX2NsdXN0ZXIucmRzIikKYGBgCgoKCiMjIyBWaXN1YWxpemluZyBUaGlzIE9iamVjdCBhcyBhIFRTTkUgRGltZW5zaW9uYWwgUGxvdApIZXJlJ3MgYSBUU05FIHBsb3QgdG8gZXhoaWJpdCBvdXIgY2x1c3RlcmluZyByZXN1bHRzOgpgYGB7cn0KVFNORVBsb3QobWFzdF9jZWxsc19jbHVzdGVyLCBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiKQpgYGAKCgpBbmQgYSBUU05FIHBsb3QgdG8gc2hvdyB0aGUgc2FtZSwgZ3JvdXBlZCBieSBoZXRlcm9nZW5vdXMgZG9ub3JzLCBhbmQgY29uZGl0aW9uczoKYGBge3J9ClRTTkVQbG90KG1hc3RfY2VsbHNfY2x1c3RlciwgZ3JvdXAuYnkgPSAiRG9ub3IiKQpgYGAKCgpgYGB7cn0KVFNORVBsb3QobWFzdF9jZWxsc19jbHVzdGVyLCBncm91cC5ieSA9ICJDb25kaXRpb24iKQpgYGAKCgojIyMgRmluZGluZyBNYXJrZXJzIGZvciBEaWZmZXJlbnRpYWwgRXhwcmVzc2VkIEdlbmVzCk5vdyB3ZSBuZWVkIHRvIGZpbmQgbWFya2VyIGdlbmVzIGZvciB0aGlzIHByb2Nlc3NlZCBtYXN0IGNlbGwgY2x1c3RlciBvYmplY3QgdW5kZXIgdGhlIDMgY2x1c3RlcnMgaWRlbnRpZmllZC4gT2J0YWluaW5nIHRoZXNlIG1hcmtlciBnZW5lcyB3aWxsIGhlbHAgdXMgZG93bnN0cmVhbSBpbiBzZWVpbmcgd2hpY2ggZ2VuZXMgYXJlIGV4cHJlc3NlZCB3aGVuIGFuZCB3aHkuIFRoZXkgYXJlIGNhbGxlZCBkaWZmZXJlbnRpYWwgZXhwcmVzc2VkIGdlbmVzIChERUcpLgpgYGB7ciwgZWNobz1UUlVFLCByZXN1bHRzPSdoaWRlJ30KSWRlbnRzKG1hc3RfY2VsbHNfY2x1c3RlcikgPC0gInNldXJhdF9jbHVzdGVycyIKbWFzdF9jZWxsX21hcmtlcnMgPC0gIFNldXJhdDo6RmluZEFsbE1hcmtlcnMobWFzdF9jZWxsc19jbHVzdGVyLCBvbmx5LnBvcyA9IEZBTFNFLCB0ZXN0LnVzZSA9ICJyb2MiKQpgYGAKCgpTYXZlIG1hcmtlcnMgYXMgdGFibGUuCmBgYHtyfQp3cml0ZS5jc3YobWFzdF9jZWxsX21hcmtlcnMsICJtYXN0X2NlbGxfbWFya2Vyc19jbGVhbi5jc3YiKQpoZWFkKG1hc3RfY2VsbF9tYXJrZXJzX2NsZWFuKQpgYGAKCgoKIyMjIENsYXNzaWZ5aW5nIERFRyBNYXJrZXJzIEJhc2VkIG9uIERpYWJldGljIENvbmRpdGlvbnM6CkNoYW5nZSBJZGVudHMoKSB0byBsb29rIGF0IGRpZmZlcmVudGlhbCBleHByZXNzZWQgZ2VuZXMgYnkgY29uZGl0aW9uLCBiZWNhdXNlIHdlIHdhbnQgdG8gdmlldyB0aGUgbWFya2VyIGdlbmVzIHdlIG9idGFpbmVkIGJlZm9yZSBiYXNlZCBvbiBMZWFuIGNvbmRpdGlvbiAod2hpY2ggaXMgbm9uLWRpYWJldGljKSwgYW5kIFQyRCBjb25kaXRpb24uIFdlIGlnbm9yZSBPYmVzZSBncm91cCBiZWNhdXNlIGl0IGNvbnRhaW5zIGJvdGggZGlhYmV0aWMgYW5kIG5vbi1kaWFiZXRpYyBzYW1wbGVzLCB3aGljaCBoYXMgc2hvd24gdG8gcHJvZHVjZSBkaXNjcmVwYW5jeSBpbiByZXN1bHRzLgpgYGB7cn0KSWRlbnRzKG1hc3RfY2VsbHNfY2x1c3RlcikgPC0gIkNvbmRpdGlvbiIKbGV2ZWxzKG1hc3RfY2VsbHNfY2x1c3RlcikKYGBgCgoKCiMjIyBWaXN1YWxpemluZyBTdWNoIE1hcmtlcnMgdGhyb3VnaCBUU05FIERpbWVuc2lvbmFsIFBsb3RzCkxldCdzIG5vdyB2aWV3IHRoZSBhbmFseXNpcyB3ZSd2ZSBkb25lIHVzaW5nIFRTTkVQbG90LiBXZSBmaXJzdCBvYnRhaW4gYSBnZW5lcmFsIHBsb3Qgb2YgYWxsIGNvbmRpdGlvbnMgZm9yIHJlZmVyZW5jZS4gWW91IGNhbiBhbHNvIGRvIHRoaXMgdXNpbmcgVU1BUFBsb3Qgb3IgRGltUGxvdC4KYGBge3J9ClRTTkVQbG90KG1hc3RfY2VsbHNfY2x1c3Rlcixncm91cC5ieSA9ICJDb25kaXRpb24iKQpgYGAKCgoKIyMjIFN1YnNldHRpbmcgTWFya2VycyBpbnRvIERpYWJldGljIENvbmRpdGlvbnMgb2YgSW50ZXJlc3QKV2UgZnVydGhlciBzdWJzZXQgdGhlIHNldXJhdCBvYmplY3QgbWFzdF9jZWxsc19jbHVzdGVyIGJhc2VkIG9uIHRoZSBjb25kaXRpb25zIHdlIHdhbnQsIHdoaWNoIGFyZSBsZWFuIGFuZCBUMkQuIFdlIHRoZW4gcmVwcm9jZXNzIGNvbmRpdGlvbl9zdWJzZXQgaW50byBjbHVzdGVycyBmb3IgYWNjdXJhdGUgdmlzdWFsaXphdGlvbi4KYGBge3J9Cm1hc3RfY2VsbHNfY2x1c3RlckBncmFwaHMgPC0gbGlzdCgpCmNvbmRpdGlvbl9zdWJzZXQgPC0gc3Vic2V0KG1hc3RfY2VsbHNfY2x1c3RlciwgaWRlbnRzID0gYygiTGVhbiIsICJUMkQiKSkKY29uZGl0aW9uX3N1YnNldCA8LSBSdW5QQ0EoY29uZGl0aW9uX3N1YnNldCwgZGltcyA9IDE6MzAsIHZlcmJvc2UgPSBGQUxTRSkgJT4lCiAgUnVuVFNORShkaW1zID0gMTozMCwgdmVyYm9zZSA9IEZBTFNFKSAlPiUKICBGaW5kTmVpZ2hib3JzKHZlcmJvc2UgPSBUUlVFKSAlPiUKICBGaW5kQ2x1c3RlcnMocmVzb2x1dGlvbiA9IDAuMSwgdmVyYm9zZSA9IFRSVUUpClRTTkVQbG90KGNvbmRpdGlvbl9zdWJzZXQpK2dndGl0bGUoIkxlYW4gYW5kIFQyRCBDb25kaXRpb25zIikKYGBgCgojIyMgRmluZGluZyBERUdzIEJhc2VkIG9uIHRoZXNlIFNwZWNpZmljIERpYWJldGljIENvbmRpdGlvbnMKRmluZCBERUdzIGJldHdlZW4gTGVhbiBhbmQgVDJEIGNvbmRpdGlvbnMuCmBgYHtyLCBlY2hvPVRSVUUsIHJlc3VsdHM9J2hpZGUnfQpjb25kaXRpb25fc3Vic2V0IDwtIFNldXJhdDo6RmluZEFsbE1hcmtlcnMoY29uZGl0aW9uX3N1YnNldCwgb25seS5wb3MgPSBGQUxTRSwgdGVzdC51c2UgPSAicm9jIikKYGBgCgpUaGlzIGNhbiBiZSBzYXZlZCBib3RoIGFzIGEgQ1NWIGZvciBsYXRlciB1c2Ugb24gRXhjZWwuCmBgYHtyfQp3cml0ZS5jc3YoY29uZGl0aW9uX3N1YnNldCwgImNvbmRpdGlvbl9zdWJzZXQuY3N2IikKYGBgCgoKIyMjIE9yZ2FuaXppbmcgREVHIExpc3RzCkluIHRoaXMgZmluYWwgc3RlcCwgdGhlIERFRyBsaXN0IHdlJ3ZlIG9idGFpbmVkLCBjb25kaXRpb25fc3Vic2V0LCBuZWVkcyB0byBiZSBvcmdhbml6ZWQgYmV0dGVyIGZvciBlYXNpZXIgYW5hbHlzaXMuIFdlIG9yZ2FuaXplIGl0IGluIHR3byB3YXlzLiBGaXJzdCBpcyBiYXNlZCBvbiBhdmVyYWdlIGxvZzJGQywgYmVjYXVzZSBpbmNyZWFzaW5nIHBvc2l0aXZlIGxvZyBmb2xkIGNoYW5nZSBkZW5vdGVzIHVwIHJlZ3VsYXRpb24gaW4gZ2VuZXMuIFNlY29uZCBpcyBiYXNlZCBvbiByZW1vdmluZyBkaXNjcmVwYW50IGdlbmVzIHN1Y2ggYXMgbWl0b2Nob25kcmlhbCAoTVQpIGFuZCByaWJvc29tZSAoUlApIGdlbmVzIHVzaW5nIHRoZSBmdW5jdGlvbiAhZ3JlcGwuCmBgYHtyfQpjb25kaXRpb25fc3Vic2V0JGdlbmVzIDwtIHJvd25hbWVzKGNvbmRpdGlvbl9zdWJzZXQpCmNvbmRpdGlvbl9zdWJzZXQgPC0gZHBseXI6OmZpbHRlcihjb25kaXRpb25fc3Vic2V0LCAhZ3JlcGwoIk1ULSIsIGNvbmRpdGlvbl9zdWJzZXQkZ2VuZXMpKQpjb25kaXRpb25fc3Vic2V0IDwtIGRwbHlyOjpmaWx0ZXIoY29uZGl0aW9uX3N1YnNldCwgIWdyZXBsKCJSUCIsIGNvbmRpdGlvbl9zdWJzZXQkZ2VuZXMpKQpzb3J0ZWRfbWFzdF9jZWxsc19jb25kaXRpb25fc3Vic2V0IDwtIGNvbmRpdGlvbl9zdWJzZXRbb3JkZXIoY29uZGl0aW9uX3N1YnNldCRhdmdfbG9nMkZDLCBkZWNyZWFzaW5nID0gVFJVRSksIF0KCmhlYWQoc29ydGVkX21hc3RfY2VsbHNfY29uZGl0aW9uX3N1YnNldCkKYGBgCgojIyMgU2F2aW5nIGFuZCBWaWV3aW5nIGFuIE9yZ2FuaXplZCBERUcgVGFibGU6CkFmdGVyIG9yZ2FuaXppbmcgdGhyb3VnaCBhdmVyYWdlIGxvZzJGQyBhbmQgZGlzY3JlcGFudCBnZW5lcywgd2Ugc2F2ZSBhcyBjc3YgZmlsZSBhbmQgZXhwb3J0IHRvIEV4Y2VsIGZvciBmdXJ0aGVyIGFuYWx5c2lzLgpgYGB7cn0KVmlldyhzb3J0ZWRfbWFzdF9jZWxsc19jb25kaXRpb25fc3Vic2V0KQp3cml0ZS5jc3Yoc29ydGVkX21hc3RfY2VsbHNfY29uZGl0aW9uX3N1YnNldCwgInNvcnRlZF9tYXN0X2NlbGxzX2NvbmRpdGlvbl9zdWJzZXQuY3N2IikKYGBgCg==